package com.jsftoolkit.gen;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import javax.faces.component.UIComponent;

import com.jsftoolkit.gen.info.ComponentInfo;
import com.jsftoolkit.gen.info.ConstantInfo;
import com.jsftoolkit.gen.info.PropertyInfo;
import com.jsftoolkit.gen.writer.ComponentWriter;

/**
 * Generates the {@link UIComponent} class.
 * 
 * @author noah
 * 
 */
public class ComponentGeneratorImpl extends
		AbstractComponentClassGenerator<ComponentWriter> {

	/**
	 * Create a new component generator.
	 * 
	 * @param componentInfo
	 *            the metadata to use
	 */
	public ComponentGeneratorImpl() {
		super();
	}

	public void generate(ComponentInfo info, ComponentWriter writer) {

		// make a copy for us to alter
		Map<String, PropertyInfo> properties = new HashMap<String, PropertyInfo>(
				info.getProperties());

		// make sure the listener classes get imported
		info.getImports().addAll(info.getListeners());

		// declaration
		printToBody(writer, info, info.getSuperClass());

		// pass through the property constants
		for (Entry<String, PropertyInfo> prop : properties.entrySet()) {
			writer
					.printStringConstant(prop.getKey(), prop.getValue()
							.getName());
		}

		// remove the properties with a null class, since we can't give them
		// getters/setters
		Iterator<Entry<String, PropertyInfo>> it = properties.entrySet()
				.iterator();
		while (it.hasNext()) {
			if (it.next().getValue().getType() == null) {
				it.remove();
			}
		}

		// pass through the component constants
		for (ConstantInfo constant : info.getConstants()) {
			writer.printStringConstant(constant.getName(), constant.getValue());
		}

		// write all the type constants, if they are defined
		if (info.getType() == null && !info.isAbstract()) {
			throw new IllegalArgumentException(
					"Component must be abstract or specify COMPONENT_TYPE");
		}
		writer.printCoreConstants(info.getRendererType(), info.getFamily(),
				info.getType());

		// declare the property fields
		for (Entry<String, PropertyInfo> prop : properties.entrySet()) {
			PropertyInfo propInfo = prop.getValue();
			writer.printPropertyField(propInfo.getType(), propInfo.getName());
		}

		// create the constructor
		writer.printConstructor();

		// create the getter for component family
		writer.printFamilyGetter();

		// create getters and setters for the properties
		for (Entry<String, PropertyInfo> prop : properties.entrySet()) {
			PropertyInfo propInfo = prop.getValue();
			writer.printAccessors(propInfo.getType(), propInfo.getName(),
					propInfo.getDefaultValue(), prop.getKey(), propInfo
							.isDefaultSaved());
		}

		// write the save and restore methods
		writer.printSaveAndRestore();

		writer.printListenerCode(info.getListeners());

		// write the helper methods
		writer.close();
	}

}
